{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5 Decorators and Special Methods "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1 Decorator Semantics"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Decorators are callables that can modify the function or class they\n",
    "decorate.  We'll focus on function decorators implemented as functions."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Conceptually a decorator changes or adds to the behaviour of a\n",
    "function either by modifying its arguments before the function is\n",
    "called, changing its return value afterwards, or both."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Here's a simple function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(first, second):\n",
    "    \"\"\"Return the sum of two arguments.\"\"\"\n",
    "    return first + second"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add(2, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Here's a simple function that creates and returns a function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_adder(first):\n",
    "    def adder(second):\n",
    "        return add(first, second)  # Notice first is used here!\n",
    "    return adder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add_2_to = create_adder(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add_2_to(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  (How does this work?)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add_2_to.__code__.co_freevars"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add_2_to.__closure__[0].cell_contents"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Next let's look at a function that accepts a function as an argument.  First the barebones version:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def trace_function(func):\n",
    "    def new_func(*args):\n",
    "        return func(*args)\n",
    "    return new_func"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Now with some print statements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def trace_function(func):\n",
    "    \"\"\"Add tracing before and after a function\"\"\"\n",
    "    def new_func(*args):\n",
    "        \"\"\"The new function\"\"\"\n",
    "        print(f'Called {func}{args!r}')\n",
    "        result = func(*args)\n",
    "        print(f'Returning {result!r}')\n",
    "        return result\n",
    "    return new_func"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  This `trace_function` wraps the functionality of the function passed\n",
    "to it by returning a new function that calls the original function,\n",
    "but prints some trace information before and after."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "traced_add = trace_function(add)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "traced_add(2, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Instead of binding a new name to the new function returned from\n",
    "`trace_function` we could instead re-bind the original name:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add = trace_function(add)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add(2, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Or we can use the decorator syntax to do that for us:  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@trace_function\n",
    "def add(first, second):\n",
    "    \"\"\"Return the sum of two arguments.\"\"\"\n",
    "    return first + second"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add(2, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__qualname__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__doc__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Use `@wraps` to update the metadata of the returned function and make it more useful."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import functools\n",
    "def trace_function(func):\n",
    "    \"\"\"Add tracing before and after a function\"\"\"\n",
    "    @functools.wraps(func)  # <-- Added\n",
    "    def new_func(*args):\n",
    "        \"\"\"The new function\"\"\"\n",
    "        print(f'Called {func}{args!r}')\n",
    "        result = func(*args)\n",
    "        print(f'Returning {result!r}')\n",
    "        return result\n",
    "    return new_func"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@trace_function\n",
    "def add(first, second):\n",
    "    \"\"\"Return the sum of two arguments.\"\"\"\n",
    "    return first + second"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__qualname__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add.__doc__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  To write a decorator that takes parameters, we need to write a\n",
    "function that is called with arguments and returns a decorator whose\n",
    "behaviour depends on those arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def better_trace_function(uppercase=False):\n",
    "    def trace_function(func):\n",
    "        \"\"\"Add tracing before and after a function\"\"\"\n",
    "        @functools.wraps(func)\n",
    "        def new_func(*args):\n",
    "            \"\"\"The new function\"\"\"\n",
    "            print(f'Called {func}{args!r}')\n",
    "            result = func(*args)\n",
    "            print(f'Returning {result!r}')\n",
    "            if uppercase:              # Two new\n",
    "                return result.upper()  # lines\n",
    "            return result\n",
    "        return new_func\n",
    "    return trace_function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@better_trace_function(uppercase=False)\n",
    "def concat(s, t):\n",
    "    return s + t"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "concat('spam', 'eggs')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@better_trace_function(uppercase=True)\n",
    "def concat(s, t):\n",
    "    return s + t"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "concat('spam', 'eggs')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  What will Python do with the following code?  Don't think about how\n",
    "they're usually used.  Think about how they work in Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def return_spam(func):\n",
    "    print(f'Called return_spam({!func})'\n",
    "    return 'spam'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@return_spam\n",
    "def add(first, second):\n",
    "    \"\"\"Return the sum of two arguments.\"\"\"\n",
    "    return first + second"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  What object will the name `add` be bound to?  What is it's type?\n",
    "Try to figure it out before executing these statements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "type(add)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(first, second):\n",
    "    \"\"\"Return the sum of two arguments.\"\"\"\n",
    "    return first + second"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add = return_spam(add)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "add"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "type(add)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  What will Python do with the following code?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def return_spam(klass):\n",
    "    \"\"\"Ignore the class argument and return 'spam'\"\"\"\n",
    "    print(f'Called return_spam({!klass})'\n",
    "    return 'spam'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@return_spam\n",
    "class WeirdClass2:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  What object will the name `WeirdClass` be bound to?  What is it's type?  Try\n",
    "to figure it out before executing these statements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "WeirdClass2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "type(WeirdClass2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "Name binding operations covered so far:\n",
    "\n",
    "  - *name* `=` (assignment)\n",
    "  - `del` *name* (unbinds the name)\n",
    "  - `def` *name* function definition (including lambdas)\n",
    "  - `def name(`*names*`):` (function execution)\n",
    "  - *name*`.`*attribute_name* `=`, `__setattr__`, `__delattr__`\n",
    "  - `global`, `nonlocal` (changes scope rules)\n",
    "  - `except Exception as` *name*:\n",
    "  - access to name bindings:\n",
    "    - `globals()[`*name*`]\n",
    "    - `locals()[`*name*`]\n",
    "  - `import` *name*\n",
    "  - `class` *name*`:`\n",
    "\n",
    "The others:\n",
    "  - `for` *name* `in ...:` (also list comprehensions and generator expressions)\n",
    "  - `with expr as` *name*`:`\n",
    "  - `setattr(an_object, `*name*`)`\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2 Special Methods of Classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "  These \"dunder\" methods are related to an object's attribute access:\n",
    "  - `__getattr__`\n",
    "  - `__getattribute__`\n",
    "  -  `__setattr__`\n",
    "  - `__delattr__`\n",
    "\n",
    "  We won't cover descriptors (`__get__`, `__set__`, and `__delete__`)\n",
    "  or `__dir__`, and note that `__del__` is not a namespace operation.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Let's look at a simple example of changing how a class handles attribute access."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class UppercaseAttributes:\n",
    "    \"\"\"\n",
    "    A class that returns uppercase values on uppercase attribute access.\n",
    "    \"\"\"\n",
    "    # Called only if attribute access fails:\n",
    "    def __getattr__(self, name):\n",
    "        if name.isupper():\n",
    "            if name.lower() in self.__dict__:\n",
    "                return self.__dict__[\n",
    "                    name.lower()].upper()\n",
    "        raise AttributeError(\n",
    "            f\"'{self}' object has no attribute {name}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ua = UppercaseAttributes()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ua.__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ua.attriibute1 = 'value1'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ua.attriibute1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ua.__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ua.ATTRIIBUTE1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ua.baz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "  To add behaviour for specific attributes you can also use properties.\n",
    "\n",
    "  Given what we've learned about decorators you may be able to infer a\n",
    "  bit about the `setter` and `deleter` attributes of the object it\n",
    "  returns.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class PropertyEg:\n",
    "    \"\"\"@property example\"\"\"\n",
    "    def __init__(self):\n",
    "        self._x = 'Uninitialized'\n",
    "    \n",
    "    @property\n",
    "    def x(self):\n",
    "        \"\"\"The 'x' property\"\"\"\n",
    "        print('Called x getter()')\n",
    "        return self._x\n",
    "    \n",
    "    @x.setter\n",
    "    def x(self, value):\n",
    "        print('Called x.setter()')\n",
    "        self._x = value\n",
    "    \n",
    "    @x.deleter\n",
    "    def x(self):\n",
    "        print('Called x.deleter')\n",
    "        self.__init__()  # Reinitialize _x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p = PropertyEg()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p._x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.x = 'bar'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "del p.x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.x = 'bar'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Usually you should just expose attributes and add properties later\n",
    "if you need some measure of control or change of behaviour."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "  Now let's look at an example of using `__getattr__`:\n",
    "\n",
    "    PYTHON_RELEASES = [\n",
    "        'Python 3.6.5 2018-03-28',\n",
    "        'Python 3.5.5 2018-02-05',\n",
    "        'Python 3.4.8 2018-02-05',\n",
    "        'Python 3.3.6 2014-10-12',\n",
    "        'Python 3.2.6 2014-10-12',\n",
    "        'Python 3.1.5 2012-04-09',\n",
    "        'Python 3.0.1 2009-02-13',\n",
    "    ]\n",
    "\n",
    "    release36 = PYTHON_RELEASES[0]\n",
    "\n",
    "    release = ReleaseFields(release36)  # 3.6.5\n",
    "    assert release.name == 'Python'\n",
    "    assert release.version == '3.6.5'\n",
    "    assert release.date == '2018-03-28'\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  First, without `__getattr__`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ReleaseFields:\n",
    "    def __init__(self, data):\n",
    "        self.data = data\n",
    "    \n",
    "    @property\n",
    "    def name(self):\n",
    "        return self.data[0:6]\n",
    "    \n",
    "    @property\n",
    "    def version(self):\n",
    "        return self.data[7:12]\n",
    "    \n",
    "    @property\n",
    "    def date(self):\n",
    "        return self.data[13:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "release36 = 'Python 3.6.5 2018-03-28'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "release = ReleaseFields(release36)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert release.name == 'Python'\n",
    "assert release.version == '3.6.5'\n",
    "assert release.date == '2018-03-28'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  However, the following is easier to extend to many fields."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ReleaseFields:\n",
    "    SLICES = {\n",
    "        'name': slice(0, 6),\n",
    "        'version': slice(7, 12),\n",
    "        'date': slice(13, None)\n",
    "        }\n",
    "    \n",
    "    def __init__(self, data):\n",
    "        self.data = data\n",
    "    \n",
    "    def __getattr__(self, attribute):\n",
    "        if attribute in self.SLICES:\n",
    "            return self.data[self.SLICES[attribute]]\n",
    "        raise AttributeError(\n",
    "        raise AttributeError(\n",
    "            f\"'{self}' object has no attribute {name}.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "release = ReleaseFields(release36)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert release.name == 'Python'\n",
    "assert release.version == '3.6.5'\n",
    "assert release.date == '2018-03-28'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  Let's confirm that trying to access an attribute that doesn't exist\n",
    "fails, as it should."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "release.foo == 'exception'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.3 Exercises: Special Methods of Classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Try the following:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class GetTracer:\n",
    "    def __getitem__(self, key):\n",
    "        print('Called __getitem__({type(key)} {key!r})')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = GetTracer()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g[0:3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g[0:10:2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g['Jan']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g[g]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m = list('abcdefghij')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m[::2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s = slice(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m[s]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m[slice(1, 3)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m[slice(0, 2)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m[slice(0, len(m), 2)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m[::2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "m[:100]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "list(zip('abc', 'abcdef'))"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 2
}